home *** CD-ROM | disk | FTP | other *** search
- /*
- * Packet filtering code for KA9Q.
- *
- * Copyright 1992 David F. Mischler
- * This code may be freely distributed as long as this copyright
- * notice is preserved.
- */
-
- /****************************************************************************
- * $Id: ipfilter.c 1.3 94/01/04 14:09:32 ROOT_DOS Exp $
- * 14 Jun 93 1.2 GT Fix warnings.
- * 07 Nov 93 1.3 GT Conditional compilation.
- *
- * ATARI Version by David Nash - dnash@chaos.demon.co.uk
- *
- * conditionally include "mbuf.h"
- *
- ****************************************************************************/
-
- #include <limits.h>
- #include "config.h"
-
- #ifdef FILTER
-
- #ifndef _MBUF_H
- #include "mbuf.h"
- #endif
-
- #ifndef _FILTER_H
- #include "filter.h"
- #endif
-
- #ifndef _IFACE_H
- #include "iface.h"
- #endif
-
- #ifndef _ICMP_H
- #include "icmp.h"
- #endif
-
- #ifndef _IP_H
- #include "ip.h"
- #endif
-
- #ifndef _NETUSER_H
- #include "netuser.h"
- #endif
-
- #include "socket.h"
-
- #define SDWIDTH 21 /* Width of a displayed source or destination */
-
- /*
- * Information for TCP header kludges.
- */
- #define CODEBITS_ACK 16
- #define CODEBITS_OFFSET 13
- #define CODEBITS_SYN 2
- #define SRC_PORT_OFFSET 0 /* TCP & UDP */
- #define DST_PORT_OFFSET 2 /* TCP & UDP */
-
-
- /*
- * Function to return a non-zero value if an IP packet
- * matches a filter entry.
- */
- static
- int
- pkt_ip( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- /*
- * If source address doesn't match then get out.
- */
- if ( fp->src.addr == ( ip->source & fp->src.mask ) ) {
- if ( fp->src.exclude )
- return 0;
- }
- else {
- if ( fp->src.exclude == 0 )
- return 0;
- }
-
- /*
- * If destination address doesn't match then get out.
- */
- if ( fp->dest.addr == ( ip->dest & fp->dest.mask ) ) {
- if ( fp->dest.exclude )
- return 0;
- }
- else {
- if ( fp->dest.exclude == 0 )
- return 0;
- }
-
- return 1;
- }
-
-
- /*
- * Function to return a non-zero value if an ICMP packet
- * matches a filter entry.
- */
- static
- int
- pkt_icmp( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- /*
- * Check protocol type.
- */
- if ( ip->protocol != ICMP_PTCL )
- return 0;
-
- /*
- * Check source & destination addresses.
- */
- return pkt_ip( bp, ip, fp );
- }
-
- /*
- * Function to return a non-zero value if an ICMP REDIRECT
- * packet matches a filter entry.
- */
- static
- int
- pkt_icmprd( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- /*
- * Check protocol type and source & destination addresses.
- */
- if ( pkt_icmp( bp, ip, fp ) == 0 )
- return 0;
-
- /*
- * Check ICMP message type.
- */
- if ( bp->data[ 0 ] == ICMP_REDIRECT )
- return 1;
-
- return 0;
- }
-
-
- /*
- * Function to return a non-zero value if an ICMP packet
- * other than a REDIRECT matches a filter entry.
- */
- static
- int
- pkt_icmpxrd( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- /*
- * Check protocol type and source & destination addresses.
- */
- if ( pkt_icmp( bp, ip, fp ) == 0 )
- return 0;
-
- /*
- * Check ICMP message type.
- */
- if ( bp->data[ 0 ] != ICMP_REDIRECT )
- return 1;
-
- return 0;
- }
-
-
- /*
- * Function to return a non-zero value if a TCP packet
- * matches a filter entry.
- */
- static
- int
- pkt_tcp( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- unsigned short port;
-
- /*
- * Check protocol type.
- */
- if ( ip->protocol != TCP_PTCL )
- return 0;
-
- /*
- * Check source & destination addresses.
- */
- if ( pkt_ip( bp, ip, fp ) == 0 )
- return 0;
-
- /*
- * Check source port.
- */
- if ( fp->src.port ) {
- port = get16( &bp->data[SRC_PORT_OFFSET] );
- if ( fp->src.port > port || fp->src.hiport < port )
- return 0;
- }
-
- /*
- * Check destination port.
- */
- if ( fp->dest.port ) {
- port = get16( &bp->data[DST_PORT_OFFSET] );
- if ( fp->dest.port > port || fp->dest.hiport < port )
- return 0;
- }
- return 1;
- }
-
-
- /*
- * Function to return a non-zero value if a TCP packet
- * with SYN set and ACK clear matches a filter entry.
- */
- static
- int
- pkt_tcpsyn( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- unsigned char codebits;
-
- /*
- * Check for TCP protocol type and matching addresses.
- */
- if ( pkt_tcp( bp, ip, fp ) == 0 )
- return 0;
-
- /*
- * Check that SYN is set and ACK is clear.
- */
- codebits = bp->data[ CODEBITS_OFFSET ];
- if ( ( codebits & CODEBITS_SYN ) && ( codebits & CODEBITS_ACK ) == 0 )
- return 1;
-
- return 0;
- }
-
-
- /*
- * Function to return a non-zero value if a TCP packet
- * with SYN clear or ACK set matches a filter entry.
- */
- static
- int
- pkt_tcpxsyn( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- unsigned char codebits;
-
- /*
- * Check for TCP protocol type and matching addresses.
- */
- if ( pkt_tcp( bp, ip, fp ) == 0 )
- return 0;
-
- /*
- * Check that SYN is clear or ACK is set.
- */
- codebits = bp->data[ CODEBITS_OFFSET ];
- if ( ( codebits & CODEBITS_SYN ) == 0 || ( codebits & CODEBITS_ACK ) )
- return 1;
-
- return 0;
- }
-
-
- /*
- * Function to return a non-zero value if a UDP packet
- * matches a filter entry.
- */
- static
- int
- pkt_udp( struct mbuf *bp, struct ip *ip, struct filter *fp )
- {
- unsigned short port;
-
- /*
- * Check protocol type.
- */
- if ( ip->protocol != UDP_PTCL )
- return 0;
-
- /*
- * Check source & destination addresses.
- */
- if ( pkt_ip( bp, ip, fp ) == 0 )
- return 0;
-
- /*
- * Check source port.
- */
- if ( fp->src.port ) {
- port = get16( &bp->data[SRC_PORT_OFFSET] );
- if ( fp->src.port > port || fp->src.hiport < port )
- return 0;
- }
-
- /*
- * Check destination port.
- */
- if ( fp->dest.port ) {
- port = get16( &bp->data[DST_PORT_OFFSET] );
- if ( fp->dest.port > port || fp->dest.hiport < port )
- return 0;
- }
- return 1;
- }
-
-
- /*
- * Table of filter actions (indexes must match FILTER_ACTION_*).
- */
- static char *Acts[] = {
- "?", "deny", "permit"
- };
-
- /*
- * Table of packet type names and matching functions.
- */
- static struct {
- char *name;
- int (*function) __ARGS((struct mbuf *bp, struct ip *ip, struct filter *fp));
- } Types[] = {
- { "*", pkt_ip }, /* Any IP packet */
- { "icmp", pkt_icmp }, /* Any ICMP packet */
- { "icmprd", pkt_icmprd }, /* ICMP redirect */
- { "icmpxrd", pkt_icmpxrd }, /* ICMP except redirect */
- { "tcp", pkt_tcp }, /* Any TCP packet */
- { "tcpsyn", pkt_tcpsyn }, /* TCP SYN packet */
- { "tcpxsyn", pkt_tcpxsyn }, /* TCP except SYN */
- { "udp", pkt_udp }, /* Any UDP packet */
- { NULL, NULL }
- };
-
- /*
- * Function to parse a filter source or destination spec.
- * A non-zero return value indicates a syntax problem.
- */
- static
- int
- sdparse( char *p, struct filtersd *sdp )
- {
- char *addrp; /* Pointer to address specification */
- char *bitp; /* Pointer to bit count */
-
- sdp->addr = 0L;
- sdp->mask = ~0L;
- sdp->bits = 32;
- sdp->exclude = 0;
- sdp->port = 0;
- sdp->hiport = 0;
-
- /*
- * Look for '!' in address to specify address exclusion.
- */
- if ( *p == '!' ) {
- p += 1;
- sdp->exclude = 1;
- }
- addrp = p;
-
- /*
- * Look for '/' in spec to separate number of bits.
- */
- if ((bitp = strchr( p, '/' )) != 0) {
- *bitp++ = '\0';
- p = bitp;
- }
-
- /*
- * Look for ':' in spec to separate port number.
- */
- if ((p = strchr( p, ':' )) != 0) {
- *p++ = '\0';
- sdp->hiport = sdp->port = atoi( p );
-
- /*
- * '+' in port spec indicates >= port.
- */
- if ( strchr( p, '+' ) )
- sdp->hiport = USHRT_MAX;
- /*
- * '-' in port spec indicates port range.
- */
- else if ((p = strchr( p, '-' )) != 0) {
- p += 1;
- sdp->hiport = atoi( p );
- }
- if ( sdp->port > sdp->hiport ) {
- tprintf( "Bad port range\n" );
- return -1;
- }
- }
-
- /*
- * Evaluate number of bits if necessary.
- */
- if ( bitp )
- sdp->mask <<= ( 32 - (sdp->bits = atoi( bitp )) );
-
- /*
- * Evaluate host/net address.
- */
- if ( strcmp( "*", addrp ) == 0 ) {
- sdp->bits = 0;
- sdp->mask = 0L;
- }
- else {
- if ( ( sdp->addr = resolve( addrp ) ) == 0 ) {
- tprintf( Badhost, addrp );
- return -1;
- }
- }
-
- sdp->addr &= sdp->mask;
-
- return 0;
- }
-
- /*
- * Function to list a source or destination address.
- */
- static
- void
- listaddr( struct filtersd *sdp )
- {
- int i = 0;
-
- if ( sdp->exclude ) {
- tputc( '!' );
- i = 1;
- }
-
- if ( sdp->addr == 0L && sdp->bits == 0 ) {
- tputc( '*' );
- i += 1;
- }
- else {
- i += tprintf( "%s", inet_ntoa( sdp->addr ) );
- if ( sdp->bits != 32 )
- i += tprintf( "/%d", sdp->bits );
- }
-
- if ( sdp->port ) {
- i += tprintf( ":%u", sdp->port );
-
- if ( sdp->hiport == USHRT_MAX ) {
- tputc( '+' );
- i += 1;
- }
- else if ( sdp->hiport > sdp->port )
- i += tprintf( "-%u", sdp->hiport );
- }
-
- for ( ; i < SDWIDTH ; i++ )
- tputc( ' ' );
- }
-
- /*
- * Function to list a single filter entry.
- */
- static
- void
- listfilter( struct filter *fp, char *iface, char *direct )
- {
- int i;
-
- tprintf( "%s %-6s %-3s ", iface, Acts[fp->action], direct );
- for ( i = 0 ; Types[i].name ; i++ ) {
- if ( Types[i].function == fp->type ) {
- tprintf( "%-7s ", Types[i].name );
- break;
- }
- }
-
- listaddr( &fp->src );
- tputc( ' ' );
- listaddr( &fp->dest );
- tprintf( " %lu\n", fp->matches );
- }
-
- /*
- * Function to process the IP FILTER command.
- */
- int
- doipfilter( int argc, char *argv[], void *p )
- {
- int action;
- struct filter *fp; /* Filter entry pointer */
- struct filter **fpp; /* Pointer to filter entry pointer */
- struct iface *ifp; /* Interface structure pointer */
- int i;
- int (*type)();
- struct filtersd src,dest;
-
-
- /*
- * Make sure interface is good.
- */
- if (( ifp = if_lookup( argv[1] ) ) == NULLIF ) {
- tprintf( "Interface \"%s\" unknown\n", argv[1] );
- return 1;
- }
-
- /*
- * Check action.
- */
- if ( strcmp(argv[2],"delete") == 0 ) {
- /*
- * Delete entire filter set.
- */
- while ( ifp->infilter ) {
- fp = ifp->infilter;
- ifp->infilter = fp->next;
- free( (char *) fp );
- }
- while ( ifp->outfilter ) {
- fp = ifp->outfilter;
- ifp->outfilter = fp->next;
- free( (char *) fp );
- }
- return 0;
- }
- else if ( strcmp(argv[2],"list") == 0 ) {
- /*
- * List entire filter set.
- */
- fp = ifp->infilter;
- while ( fp ) {
- listfilter( fp, ifp->name, "in" );
- fp = fp->next;
- }
- fp = ifp->outfilter;
- while ( fp ) {
- listfilter( fp, ifp->name, "out" );
- fp = fp->next;
- }
- return 0;
- }
- else if ( strcmp(argv[2],"deny") == 0 ) {
- action = FILTER_ACTION_DENY;
- }
- else if ( strcmp(argv[2],"permit") == 0 ) {
- action = FILTER_ACTION_PERMIT;
- }
- else {
- tprintf( "Unknown action \"%s\"\n", argv[2] );
- return 2;
- }
-
- /*
- * Complain if not enough arguments.
- */
- if ( argc < 7 ) {
- tprintf("ip filter <iface> <act> <dir> <type> <src> <dest>\n");
- return -1;
- }
-
- /*
- * Check direction.
- */
- if ( strncmp(argv[3],"in",2) == 0 ) {
- fpp = &ifp->infilter;
- }
- else if ( strncmp(argv[3],"out",3) == 0 ) {
- fpp = &ifp->outfilter;
- }
- else {
- tprintf( "Unknown direction \"%s\"\n", argv[3] );
- return 3;
- }
-
- /*
- * Check packet type.
- */
- for ( i = 0 ; Types[i].name ; i++ ) {
- if ( strcmp(argv[4],Types[i].name) == 0 ) {
- type = Types[i].function;
- break;
- }
- }
- if ( Types[i].name == NULL ) {
- tprintf( "Unknown packet type \"%s\"\n", argv[4] );
- return 4;
- }
-
- /*
- * Parse source specification.
- */
- if ( sdparse( argv[5], &src ) )
- return 5;
-
- /*
- * Parse destination specification.
- */
- if ( sdparse( argv[6], &dest ) )
- return 6;
-
- /*
- * Append filter entry to list.
- */
- while ( *fpp )
- fpp = &((*fpp)->next);
-
- *fpp = fp = (struct filter *) callocw( 1, sizeof( struct filter ) );
- fp->action = action;
- fp->type = type;
- fp->src = src;
- fp->dest = dest;
-
- return 0;
- }
-
- /*
- * Function to apply a filter specification to a packet.
- * A non-zero return indicates that the packet should be dropped.
- */
- int
- ip_filter(struct mbuf *bp, struct ip *ip, struct filter *fp)
- {
- /*
- * Pass all IP fragments except the first.
- */
- if ( ip->offset != 0 )
- return 0;
-
- /*
- * Walk the filter list until an entry matches the packet.
- */
- for ( ; fp ; fp = fp->next ) {
- /*
- * If packet doesn't match try the next entry.
- */
- if ( (*fp->type)( bp, ip, fp ) == 0 )
- continue;
-
- fp->matches += 1;
-
- /*
- * Take specified action.
- */
- if ( fp->action == FILTER_ACTION_PERMIT )
- return 0;
- return -1;
- }
- return 1; /* Deny all packets not explicitly permitted */
- }
-
- #endif /* ifdef FILTER */
-